﻿// Code file original author: Martin Lapierre, http://devinstinct.com

using System;
using System.Text;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using EntLibContrib.Validation;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Practices.EnterpriseLibrary.Validation;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;

namespace EntLibContrib.Validation.Tests
{
    /// <summary>
    /// Tests <see cref="ArgumentValidationException"/>.
    /// </summary>
    [TestClass()]
    public class ArgumentValidationExceptionFixture
    {
        /// <summary>
        /// Tests an unsafe exception construction.
        /// </summary>
        /// <remarks>
        /// An unsafe exception propagates the argument value and validation results.
        /// </remarks>
        [TestMethod()]
        public void ConstructUnsafeException()
        {
            string paramName = "param";
            string paramValue = "value";
            string message = "message";
            NotNullValidator validator = new NotNullValidator(true);
            ValidationResults validationResults = validator.Validate(paramValue);
            ArgumentValidationException exception = new ArgumentValidationException(message, paramName, paramValue, validationResults);

            Assert.IsFalse(exception.IsSafe);
            Assert.IsTrue(exception.Message.StartsWith(message));
            Assert.AreEqual<string>(paramName, exception.ParamName);
            Assert.AreEqual<string>(paramValue, exception.ParamString);
            Assert.AreSame(paramValue, exception.Param);
            Assert.AreSame(validationResults, exception.ValidationResults);

            AssertValidationMessages(exception.ValidationMessages, validationResults);
        }

        /// <summary>
        /// Tests a safe exception construction.
        /// </summary>
        /// <remarks>
        /// A safe exception doesn't propagate the argument value and validation results.
        /// </remarks>
        [TestMethod()]
        public void ConstructSafeException()
        {
            string paramName = "param";
            string paramValue = "value";
            string message = "message";
            NotNullValidator validator = new NotNullValidator(true);
            ValidationResults validationResults = validator.Validate(paramValue);
            ArgumentValidationException exception = new ArgumentValidationException(message, paramName, validationResults);

            Assert.IsTrue(exception.IsSafe);
            Assert.IsTrue(exception.Message.StartsWith(message));
            Assert.AreEqual<string>(paramName, exception.ParamName);
            Assert.IsNull(exception.ParamString);
            Assert.IsNull(exception.Param);
            Assert.IsNull(exception.ValidationResults);

            AssertValidationMessages(exception.ValidationMessages, validationResults);
        }

        /// <summary>
        /// Tests exception serialization.
        /// </summary>
        [TestMethod()]
        public void SerializeException()
        {
            string paramName = "param";
            string paramValue = "value";
            string message = "message";
            NotNullValidator validator = new NotNullValidator(true);
            ValidationResults validationResults = validator.Validate(paramValue);
            ArgumentValidationException exception = new ArgumentValidationException(message, paramName, paramValue, validationResults);
            
            ArgumentValidationException deserializedException = null;
            using (Stream stream = new MemoryStream())
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, exception);
                stream.Position = 0; // Reset stream position
                deserializedException = (ArgumentValidationException)formatter.Deserialize(stream);
            }

            Assert.AreEqual<bool>(exception.IsSafe, deserializedException.IsSafe);
            Assert.AreEqual<string>(exception.Message, deserializedException.Message);
            Assert.AreEqual<string>(exception.ParamName, deserializedException.ParamName);
            Assert.AreEqual<string>(exception.ParamString, deserializedException.ParamString);
            Assert.IsNull(deserializedException.Param); // Not serialized.
            Assert.IsNull(deserializedException.ValidationResults); // Not serialized.

            // Test validation messages.
            Assert.IsNotNull(exception.ValidationMessages);
            Assert.IsNotNull(deserializedException.ValidationMessages);
            Assert.AreEqual<int>(exception.ValidationMessages.Length, deserializedException.ValidationMessages.Length);
            for(int messageIndex = 0; messageIndex < exception.ValidationMessages.Length; messageIndex++)
                Assert.AreEqual<string>(exception.ValidationMessages[messageIndex], deserializedException.ValidationMessages[messageIndex]);
        }

        /// <summary>
        /// Asserts for ValidationMessage from construction.
        /// </summary>
        /// <param name="exception">The messages to validate.</param>
        /// <param name="validationResults">The source validation results.</param>
        private void AssertValidationMessages(string[] validationMessages, ValidationResults validationResults)
        {
            Assert.IsNotNull(validationMessages);

            int resultIndex = 0;
            foreach (ValidationResult result in validationResults)
            {
                Assert.IsTrue(resultIndex <= validationMessages.Length);
                Assert.AreEqual<string>(validationMessages[resultIndex], result.Message);
                resultIndex++;
            }
            Assert.AreEqual<int>(resultIndex, validationMessages.Length);
        }
    }
}
